// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <nebula/types/meta/dictionary_type.h>
#include <nebula/types/type_traits.h>
#include <turbo/log/logging.h>

namespace nebula {


    constexpr Type::type DictionaryType::type_id;

    // ----------------------------------------------------------------------
    // Dictionary-encoded type

    turbo::Status DictionaryType::ValidateParameters(const DataType &index_type,
                                                     const DataType &value_type) {
        if (!is_integer(index_type.id())) {
            return turbo::failed_precondition_error("Dictionary index type should be integer, got ",
                                                    index_type.to_string());
        }
        return turbo::OkStatus();
    }

    int DictionaryType::bit_width() const {
        return turbo::checked_cast<const FixedWidthType &>(*index_type_).bit_width();
    }

    turbo::Result<std::shared_ptr<DataType>> DictionaryType::create(
            const std::shared_ptr<DataType> &index_type,
            const std::shared_ptr<DataType> &value_type, bool ordered) {
        TURBO_RETURN_NOT_OK(ValidateParameters(*index_type, *value_type));
        return std::make_shared<DictionaryType>(index_type, value_type, ordered);
    }

    DictionaryType::DictionaryType(const std::shared_ptr<DataType> &index_type,
                                   const std::shared_ptr<DataType> &value_type, bool ordered)
            : FixedWidthType(Type::DICTIONARY),
              index_type_(index_type),
              value_type_(value_type),
              ordered_(ordered) {
        KCHECK_OK(ValidateParameters(*index_type_, *value_type_));
    }

    DataTypeLayout DictionaryType::layout() const {
        auto layout = index_type_->layout();
        layout.has_dictionary = true;
        return layout;
    }

    std::string DictionaryType::to_string(bool show_metadata) const {
        std::stringstream ss;
        ss << this->name() << "<values=" << value_type_->to_string(show_metadata)
           << ", indices=" << index_type_->to_string(show_metadata) << ", ordered=" << ordered_
           << ">";
        return ss.str();
    }

    std::string DictionaryType::compute_fingerprint() const {
        const auto &index_fingerprint = index_type_->fingerprint();
        const auto &value_fingerprint = value_type_->fingerprint();
        std::string ordered_fingerprint = ordered_ ? "1" : "0";

        DKCHECK(!index_fingerprint.empty());  // it's an integer type
        if (!value_fingerprint.empty()) {
            return type_id_fingerprint(*this) + index_fingerprint + value_fingerprint +
                   ordered_fingerprint;
        }
        return ordered_fingerprint;
    }
}  // namespace nebula